package com.example.security1.config.filter;

import com.example.security1.config.exception.CustomerAuthenticationException;
import com.example.security1.config.jwt.JwtUtils;
import com.example.security1.config.redis.RedisService;
import com.example.security1.config.security.handler.LoginFailureHandler;
import com.example.security1.config.security.user_detail.CustomerUserDetailsService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.NonNull;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Data
@Component("checkTokenFilter")
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class CheckTokenFilter extends OncePerRequestFilter {

    private final JwtUtils jwtUtils;
    private final CustomerUserDetailsService userDetailsService;
    private final LoginFailureHandler loginFailureHandler;
    private final RedisService redisService;

    @Value("${login.loginUrl}")
    private String loginUrl;

    @Override
    protected void doFilterInternal(@NonNull HttpServletRequest request,
                                    @NonNull HttpServletResponse response,
                                    @NonNull FilterChain filterChain) throws IOException, ServletException {
        // swagger相关不需要token验证
        if (!StringUtils.contains(request.getServletPath(), "swagger")
                && !StringUtils.contains(request.getServletPath(), "webjars")
                && !StringUtils.contains(request.getServletPath(), "v3")
                && !StringUtils.contains(request.getServletPath(), "profile")
                && !StringUtils.contains(request.getServletPath(), "swagger-ui")
                && !StringUtils.contains(request.getServletPath(), "swagger-resources")
                && !StringUtils.contains(request.getServletPath(), "csrf")
                && !StringUtils.contains(request.getServletPath(), "favicon")
                && !StringUtils.contains(request.getServletPath(), "v2")) {
            try {
                // 获取请求的url
                String url = request.getRequestURI();
                // token验证
                // 登录不做token验证
                if (!url.equals(loginUrl)) {
                    validateToken(request);
                }
            } catch (AuthenticationException e) {
                loginFailureHandler.onAuthenticationFailure(request, response, e);
                return;
            }
            filterChain.doFilter(request, response);
        }
    }

    private void validateToken(HttpServletRequest request) {
        // 从头部获取token
        String token = request.getHeader("token");
        // 如果从头部获取token失败，那么从参数获取
        if (StringUtils.isEmpty(token)) {
            token = request.getParameter("token");
        }
        // 如果没有获取到token
        if (StringUtils.isEmpty(token)) {
            throw new CustomerAuthenticationException("token获取失败");
        }
        // 看redis里面是否有token
        String tokenKey = "token_" + token;
        String redisToken = redisService.get(tokenKey);
        // 如果redis里面没有token，说明该token失效
        if (StringUtils.isEmpty(redisToken)) {
            throw new CustomerAuthenticationException("token过期");
        }
        if (!token.equals(redisToken)) {
            throw new CustomerAuthenticationException("token验证失败");
        }
        // 解析token
        String username = jwtUtils.getUsernameFromToken(token);
        if (StringUtils.isEmpty(username)) {
            throw new CustomerAuthenticationException("token解析失败");
        }
        // 获取用户信息
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails == null) {
            throw new CustomerAuthenticationException("token解析失败");
        }
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        // 设置到spring security 上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}
